package com.lvmama.task.job.thread;

import com.lvmama.comm.TaskServiceInterface;
import com.lvmama.comm.pet.po.pub.TaskResult;
import com.lvmama.task.common.service.TaskService;
import com.lvmama.task.common.utils.TaskUtil;
import com.lvmama.task.common.vo.Task;
import com.lvmama.task.common.vo.TaskJobLog;
import com.lvmama.task.job.common.TaskJobParameters;
import org.apache.commons.httpclient.DefaultHttpMethodRetryHandler;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.params.HttpClientParams;
import org.apache.commons.httpclient.params.HttpConnectionManagerParams;
import org.apache.commons.httpclient.params.HttpConnectionParams;
import org.apache.commons.httpclient.params.HttpMethodParams;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.params.CoreConnectionPNames;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.xfire.client.Client;
import org.codehaus.xfire.client.XFireProxyFactory;
import org.codehaus.xfire.service.Service;
import org.codehaus.xfire.service.binding.ObjectServiceFactory;
import org.codehaus.xfire.transport.http.CommonsHttpMessageSender;

import java.util.Date;

/**
 * Created by IntelliJ IDEA.<p/>
 * User: troy-kou<p/>
 * Date: 14-1-7<p/>
 * Time: 上午10:12<p/>
 * Email:kouhongyu@163.com<p/>
 */
public class TaskThread implements Runnable {

    public void run() {
        /**
         * Task 成功结束时间(nextRunTime)
         */
        long successfulEndTime;

        try {
            /**
             * Task 状态修改为开始运行
             */
            task.setStatus(Task.TASK_STATUS.RUN.getCode());
            taskService.saveTaskRuntime(task);

            /**
             * 任务日志
             */
            TaskJobLog taskJobLog = new TaskJobLog();

            taskJobLog.setTaskId(task.getTaskId());
            taskJobLog.setTaskName(task.getTaskName());
            taskJobLog.setResultRunStatus(TaskResult.RUN_STATUS.RUNNING);//运行中
            taskJobLog.setStartTime(new Date(System.currentTimeMillis()));//任务开始时间

            try {
                taskJobLog = taskService.addTaskJobLog(taskJobLog);
            } catch (Throwable e) {
                LOG.error(e.getMessage());
            }

            try {
                //############################## 运行任务 ############################## start
                LOG.info("Run_task:" + task.getTaskId() + "," + task.getTaskName());

                TaskResult taskResult = new TaskResult();

                if (Task.TASK_REQUEST_TYPE.WEB_SERVICE.equals(task.getRequestType())) {
                    taskResult = requestWebService(taskJobLog.getLogId());
                } else if (Task.TASK_REQUEST_TYPE.RESTful.equals(task.getRequestType())) {
                    taskResult = requestRESTful(taskJobLog.getLogId());
                }


                if (taskResult.getRunStatus() == null) {
                    throw new Exception("TaskResult.runStatus is null !!!,taskId:" + task.getTaskId());
                }
                //任务返回状态
                taskJobLog.setResultRunStatus(taskResult.getRunStatus());

                //任务返回信息
                taskJobLog.setResultInfo(taskResult.getResult());

                //############################## 运行任务 ############################## end
            } catch (Exception e) {
                LOG.error(e);
                //任务异常信息
                taskJobLog.setResultRunStatus(TaskResult.RUN_STATUS.FAILED);//失败
                taskJobLog.setExceptionInfo(ExceptionUtils.getStackTrace(e));
            }

            //############################## taskJobLog ############################## start
            //任务结束时间
            taskJobLog.setEndTime(new Date(System.currentTimeMillis()));
            taskJobLog.setTimeConsuming(taskJobLog.getEndTime().getTime() - taskJobLog.getStartTime().getTime());
            try {
                taskService.updateTaskJobLog(taskJobLog);
            } catch (Throwable e) {
                LOG.error(e.getMessage() + " , taskId:" + task.getTaskId() + ";taskName:" + task.getTaskName(), e);
            }
            //############################## taskJobLog ############################## end


            //############################## task ############################## start
            /**
             * 由于可能在任务执行的时候修改任务规则，所以需要重新加载Task
             */
            task = taskService.getTask(task.getTaskId());

            successfulEndTime = task.getNextRunTime().getTime();

            /**
             * 重新计算下一次的运行时间
             */
            task.setNextRunTime(TaskUtil.calculateNextRunTime(task));
            task.setStatus(Task.TASK_STATUS.WAIT.getCode());
            task.setLastRunStatus(taskJobLog.getResultRunStatus());
            task.setLastTime(taskJobLog.getStartTime());
            task.setLastTimeConsuming(taskJobLog.getTimeConsuming());

            /**
             * 保存任务
             */
            taskService.saveTaskRuntime(task);
            /**
             * 记录正常结束的Task
             */
            TaskThreadPoolFactory.getInstance().putSuccessfulEndTask(task.getTaskId(), successfulEndTime);
            //############################## task ############################## end

        } catch (Exception e) {
            LOG.error(e);
            /**
             * 记录异常结束的Task
             */
            TaskThreadPoolFactory.getInstance().putAbnormalEndTask(task.getTaskId());
        } finally {
            /**
             * 移除运行中的Task
             */
            TaskThreadPoolFactory.getInstance().removeRunningTask(task.getTaskId());
        }
    }

    /**
     * requestWebService 任务调用
     *
     * @param logId
     * @return
     * @throws Exception
     */
    private TaskResult requestWebService(Long logId) throws Exception {
        Client client = null;

        try {
            XFireProxyFactory serviceFactory = new XFireProxyFactory();
            Service serviceModel = new ObjectServiceFactory().create(TaskServiceInterface.class);
            TaskServiceInterface taskService = (TaskServiceInterface) serviceFactory.create(serviceModel, task.getWebServiceUrl());

            //#################### client & params #################### start
            HttpClientParams httpClientParams = new HttpClientParams();
            //如果服务不需要传输大量的数据，建议关闭长连接，设置为false。否则，在业务量很大的情况下，很容易将服务端和自己都搞的很慢甚至拖死
            httpClientParams.setBooleanParameter(HttpClientParams.USE_EXPECT_CONTINUE, Boolean.FALSE);
            //设置 http client 的重试次数，默认是3次,当前是禁用掉
            httpClientParams.setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler(0, Boolean.FALSE));
            //在提交请求之前,测试连接是否可用
            httpClientParams.setBooleanParameter(CoreConnectionPNames.STALE_CONNECTION_CHECK, Boolean.TRUE);
            //定义从 ConnectionManager 管理的连接池中取出连接的超时时间
            httpClientParams.setLongParameter(HttpClientParams.CONNECTION_MANAGER_TIMEOUT, CONNECTION_MANAGER_TIMEOUT);
            //定义通过网络与服务器建立连接的超时时间
            httpClientParams.setIntParameter(HttpConnectionParams.CONNECTION_TIMEOUT, CONNECTION_TIMEOUT);
            //定义 Socket 读数据的超时时间，即从服务器获取响应数据需要等待的时间
            httpClientParams.setIntParameter(HttpClientParams.SO_TIMEOUT, generateSoTimeout(task.getSoTimeout()));


            client = Client.getInstance(taskService);

            client.setProperty(CommonsHttpMessageSender.HTTP_CLIENT_PARAMS, httpClientParams);
            //禁用保持连接，快速回收tcp连接，防止连接池爆满
            client.setProperty(CommonsHttpMessageSender.DISABLE_KEEP_ALIVE, Boolean.toString(Boolean.TRUE));

            //#################### client & params #################### end

            //运行
            return taskService.execute(logId, task.getParameter());

        } finally {
            if (client != null) {
                try {
                    client.close();
                } catch (Exception ee) {
                    LOG.error(ee);
                }
            }
        }
    }

    /**
     * RESTful 任务调用
     *
     * @param logId
     * @return
     * @throws Exception
     */
    private TaskResult requestRESTful(Long logId) throws Exception {

        PostMethod postMethod = null;
        try {
            TaskResult result;

            postMethod = new PostMethod(task.getWebServiceUrl());
            postMethod.getParams().setParameter(HttpMethodParams.HTTP_CONTENT_CHARSET, CHARACTER_ENCODING);

            HttpClient httpClient = new HttpClient();
            HttpConnectionManagerParams managerParams = httpClient.getHttpConnectionManager().getParams();
            managerParams.setConnectionTimeout(CONNECTION_TIMEOUT);
            managerParams.setSoTimeout(generateSoTimeout(task.getSoTimeout()));

            postMethod.setParameter("logId", String.valueOf(logId));
            postMethod.setParameter("parameter", task.getParameter());

            int code = httpClient.executeMethod(postMethod);

            if (code == HttpStatus.SC_OK) {
                String body = postMethod.getResponseBodyAsString();
                ObjectMapper objectMapper = new ObjectMapper();
                result = objectMapper.readValue(body, TaskResult.class);

            } else {
                throw new Exception("status=" + postMethod.getStatusLine().toString() + ";body=" + postMethod.getResponseBodyAsString());
            }

            return result;
        } finally {
            if (postMethod != null) {
                postMethod.releaseConnection();
            }
        }
    }


    private Integer generateSoTimeout(Integer soTimeout) {
        return soTimeout == null ? TaskJobParameters.TASK_JOB_DEFAULT_SO_TIME : soTimeout;
    }

    public void setTaskService(TaskService taskService) {
        this.taskService = taskService;
    }

    public Task getTask() {
        return task;
    }

    public void setTask(Task task) {
        this.task = task;
    }

    Log LOG = LogFactory.getLog(this.getClass());
    private Task task;
    private TaskService taskService;
    /**
     * 这定义了从ConnectionManager管理的连接池中取出连接的超时时间，此处设置为1秒。
     */
    private static final Long CONNECTION_MANAGER_TIMEOUT = 1000L * 1;
    /**
     * 这定义了通过网络与服务器建立连接的超时时间。
     * Httpclient包中通过一个异步线程去创建与服务器的socket连接，这就是该socket连接的超时时间，此处设置为3秒。
     */
    private static final Integer CONNECTION_TIMEOUT = 1000 * 3;

    private static final String CHARACTER_ENCODING = "UTF-8";
    private static final String CONTENT_TYPE = "Content-Type";
}
